/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-12-27     SenyPC       the first version
 */
#include "device_extend.h"
#include "drivers/pin.h"
#include "device_data.h"
#include "io_extend.h"

#define DBG_TAG "dev.extend"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#define SET_EXT_CTL(dev, val)            dev_pin_write(dev->ctl_en_pin, val)
#define SET_EXT_CS(dev, val)             dev_pin_write(dev->ext_cs_pin, val)
#define SET_EXT_CLK(dev, val)            dev_pin_write(dev->ext_clk_pin, val)
#define SET_EXT_RDY(dev, val)            dev_pin_write(dev->ext_rdy_pin, val)

#define GET_EXT_DAT(dev)                dev_pin_read(dev->ext_dat_pin)
//除了通信获取的数据之外，其他检测做二次检测
#define GET_EXT_IRQ(dev)                dev_extend_get_pin(dev->ext_irq_pin)
#define GET_EXT_CHK(dev)                dev_extend_get_pin(dev->ext_chk_pin)

static struct device_extend_type _type_ext_dev = {0};        //获取设备信息接口信息


int _ext_config_nums = 0;                               //存储外设配置数量
device_extend_config_t _ext_config = NULL;              //存储外设配置信息
struct device_extend_dev _ext_data = {0};               //存储外设数据信息

void extend_pin_rising(void *args) {
    device_extend_type_t dev = &_type_ext_dev;
    device_pin_num pin = *((device_pin_num *)args);
    (void)(dev);(void)(pin);
}

static int dev_extend_get_pin(rt_base_t pin) { //通过多次读取和延迟确保GPIO引脚的状态是稳定的
    int val = 0;
    int ret = dev_pin_read(pin);  //define dev_pin_read rt_pin_read
    dev_delay(5);
    val = dev_pin_read(pin);
    if(val == ret) {
        return ret;
    } else {
        dev_delay(5);
        return dev_pin_read(pin);
    }
    return ret;
}

static int dev_ext_init( void ) {//初始化一个设备扩展模块，配置相关的GPIO引脚，并设置初始状态。
    //通过初始化这些引脚，设备驱动程序可以在系统启动时自动配置硬件接口。
    device_extend_type_t dev = &_type_ext_dev;

    dev->status = 0;                        //初始化,表示未扫描完毕
    dev->ctl_en_pin = GET_PIN(C, 13);
    dev->ext_cs_pin = GET_PIN(D, 14);
    dev->ext_clk_pin = GET_PIN(D, 15);
    dev->ext_rdy_pin = GET_PIN(D, 10);
    dev->ext_dat_pin = GET_PIN(D, 4);
    dev->ext_irq_pin = GET_PIN(D, 11);
    dev->ext_chk_pin = GET_PIN(D, 3);

    dev_pin_mode(dev->ctl_en_pin, PIN_MODE_OUTPUT);
    dev_pin_mode(dev->ext_cs_pin, PIN_MODE_OUTPUT);
    dev_pin_mode(dev->ext_clk_pin, PIN_MODE_OUTPUT);
    dev_pin_mode(dev->ext_rdy_pin, PIN_MODE_OUTPUT);
    dev_pin_mode(dev->ext_dat_pin, PIN_MODE_INPUT_PULLUP);
    dev_pin_mode(dev->ext_irq_pin, PIN_MODE_INPUT_PULLUP);
    dev_pin_mode(dev->ext_chk_pin, PIN_MODE_INPUT_PULLUP);

    dev_pin_write(dev->ext_rdy_pin, PIN_HIGH);       //初始化时先输出高电平，从机可以用这个检测上升沿中断复位系统
    dev_pin_write(dev->ctl_en_pin, PIN_HIGH);
    dev_pin_write(dev->ext_cs_pin, PIN_HIGH);
    dev_pin_write(dev->ext_clk_pin, PIN_HIGH);
    dev_pin_write(dev->ext_rdy_pin, PIN_LOW);        //初始化完成后输出低电平

    dev->dev_ext = NULL;

    dev->irq_timeout = DEVICE_IRQ_TIMEOUT;

    return 0;
}
INIT_DEVICE_EXPORT(dev_ext_init);



static void dev_irq_timecal(void *parameter) {
    device_extend_type_t dev = (device_extend_type_t)parameter;
    if(dev->irq_timeout < DEVICE_IRQ_TIMEOUT) {
        if(0 == dev_pin_read(dev->ext_irq_pin)) {
            rt_sem_release(dev->sem_irq);
        }
        dev->irq_timeout += DEVICE_IRQ_TIME;
    }
}

static int ext_dev_list_add(device_extend_list_t *pos_list, uint16_t type,int serial) {
    device_extend_list_t list = *pos_list;
    if(NULL == list) {
        list = dev_extend_malloc(sizeof(struct device_extend_list));
        if(NULL != list) {
            list->type = type;
            list->serial = serial;
            list->pre = NULL;
            list->next = NULL;

            *pos_list = list;
        }
    } else {
        while(NULL != list->next) {
            list = list->next;
        }
        device_extend_list_t list_temp = dev_extend_malloc(sizeof(struct device_extend_list));
        if(NULL != list_temp) {
            list_temp->type = type;
            list_temp->serial = serial;
            list_temp->pre = list;
            list_temp->next = NULL;

            list->next = list_temp;
        }
    }
    return 0;
}

static int ext_dev_list_clear( device_extend_list_t *pos_list ) {
    device_extend_list_t list = *pos_list;
    if(NULL != list) {
        while(NULL != list->next) {
            list = list->next;
        }
        while(NULL != list->pre){
            device_extend_list_t list_temp = list;
            list = list_temp->pre;
            dev_extend_free(list_temp);
        }
        dev_extend_free(list);
        *pos_list = NULL;
    }
    return 0;
}

static int dev_get_ext( device_extend_type_t dev ) {
    int serial = 0;
    uint16_t type = 0;
    //拉低CTL，开始读取外设的数据
    SET_EXT_CTL(dev, 0);
    SET_EXT_CS(dev,0);
    SET_EXT_CS(dev,1);
    while(1) {
        type = 0;
        for(uint8_t i = 0; i < 8; i++) {
            SET_EXT_CLK(dev,0);
            if(GET_EXT_DAT(dev)) {
                type |= (0x0001 << i);
            }
            SET_EXT_CLK(dev,1);
        }
        if(0 == type) {
           break;
        }
        //如果最高两位均位1，标识设备的类型位16位数据
        if(0xc0 == (type & 0xc0)) {
            for(uint8_t i = 8; i < 16; i++) {
                SET_EXT_CLK(dev,0);
                if(GET_EXT_DAT(dev)) {
                    type |= (0x0001 << i);
                }
                SET_EXT_CLK(dev,1);
            }
        }
        serial++;
        LOG_D("serial:%d, type:0x%x.", serial, type);
        ext_dev_list_add(&(dev->dev_list), type, serial);
    }
    //读取完成之后拉高CTL，关闭读取
    SET_EXT_CTL(dev, 1);
    return serial;
}

static device_extend_data_t dev_ext_data_create(device_extend_data_t *pos_data, uint16_t type, int addr) {
    device_extend_data_t data = *pos_data;
    if(NULL == data) {
        data = dev_extend_malloc(sizeof(struct device_extend_data));
        if(NULL != data) {
            data->type = type;
            data->addr = addr;
            data->pre = NULL;
            data->next = NULL;

            *pos_data = data;
            return data;
        }
    } else {
        while(NULL != data->next) {
            data = data->next;
        }
        device_extend_data_t data_temp = dev_extend_malloc(sizeof(struct device_extend_data));
        if(NULL != data_temp) {
            data_temp->type = type;
            data_temp->addr = addr;
            data_temp->pre = data;
            data_temp->next = NULL;

            data->next = data_temp;
            return data_temp;
        }
    }
    return NULL;
}

int dev_ext_data_init(device_extend_type_t type_dev) {
    int dev_nums = type_dev->dev_nums;
    device_extend_list_t dev_list = type_dev->dev_list;
    int config_nums = _ext_config_nums;
    device_extend_config_t config = _ext_config;
    device_extend_dev_t data_dev = &_ext_data;
    if((0 == dev_nums) || (NULL == dev_list) ||
            (0 == config_nums) || (NULL == config)) {
        return 0;
    }
    memset(data_dev,0, sizeof(struct device_extend_dev));
    for(int i = 0; i < dev_nums; i++) {
        config = _ext_config;
        for(int j = 0; j < config_nums; j++) {
            if(dev_list->type == config->type) {
                device_extend_data_t data = dev_ext_data_create(&(data_dev->data_list), dev_list->type, 1 + type_dev->dev_nums - dev_list->serial);
                if(NULL == data) {
                    return -DEVICE_ERR_ENOMEM;
                }
                io_ext_data_init(data, config->di_nums, config->do_nums);
                //添加其他类型的数据

                data_dev->di_nums += config->di_nums;
                data_dev->do_nums += config->do_nums;
                //添加其他类型的数据

                data_dev->dev_nums++;
            }
            config = config->next;
            if(NULL == config) {
                break;
            }
        }
        dev_list = dev_list->next;
        if(NULL == dev_list) {
            break;
        }
    }
    data_dev->com_dev = type_dev->dev_ext;
    return 0;
}

static int io_ext_data_clear( device_extend_dev_t data_dev ) {
    device_extend_data_t list = data_dev->data_list;
    if(NULL != list) {
        while(NULL != list->next) {
            list = list->next;
        }
        while(NULL != list->pre){
            device_extend_data_t list_temp = list;
            list = list_temp->pre;
            dev_extend_free(list_temp);
        }
        dev_extend_free(list);
        data_dev->data_list = NULL;
    }
    memset(data_dev,0, sizeof(struct device_extend_dev));
    return 0;
}


int dev_ext_start( void ) {
    int ret = 0;
    device_extend_type_t dev = &_type_ext_dev;
    dev_delay(1000); //延时1s钟，等待扩展设备就绪
    //创建modbus master
    dev->dev_ext = modbus_rtu(MODBUS_MASTER);
    if(NULL == dev->dev_ext) {
        return -MODBUS_RT_ERROR;
    }
    if(MODBUS_RT_EOK != (ret = modbus_rtu_set_serial(dev->dev_ext, SERIAL_EXT_NAME, DEVICE_EXT_BAUD,
            DEVICE_BYTESIZE, DEVICE_PARITY, DEVICE_STOPBITS, 0))) {
        return ret;
    }
    dev_master_flag = 1;
    LOG_D("dev_ext:\"%s\",%d,%d, '%c',%d. create master.", SERIAL_EXT_NAME, DEVICE_EXT_BAUD,
                    DEVICE_BYTESIZE, DEVICE_PARITY, DEVICE_STOPBITS);

    //如果有下级存在，则需要等待下级操作完成后初始化
    if(1 == GET_EXT_CHK(dev)) {
        dev->sem_irq = rt_sem_create("sem_irq", 0, RT_IPC_FLAG_PRIO);
        //等待EXT_IRQ的信号1s钟，如果没有等到，则报错
        dev->irq_timeout = 0;
        dev->irq_timer = rt_timer_create("irq_timer", dev_irq_timecal, dev, DEVICE_IRQ_TIME, RT_TIMER_FLAG_PERIODIC);
        if (dev->irq_timer != RT_NULL)  {
            rt_timer_start(dev->irq_timer);
        }
        rt_sem_take(dev->sem_irq, DEVICE_IRQ_TIMEOUT);
        rt_timer_stop(dev->irq_timer);
        rt_timer_delete(dev->irq_timer);
        rt_sem_delete(dev->sem_irq);
        //若没有等到下级初始化完成
        if(0 != GET_EXT_IRQ(dev)) {
            LOG_E("sem_irq error.");
            return -DEVICE_ERR_EXT;
        }
        //初始化完成之后，读取下级设备信息
        dev->dev_nums = dev_get_ext(dev);
        //更新dido的数据信息
        dev_ext_data_init(dev);
    }
    //开启modbus
    if(MODBUS_RT_EOK != (ret = modbus_rtu_open(dev->dev_ext))) {
        return ret;
    }
    LOG_D("dev_ext open sucess.");
    dev->status = 1;
    return 0;
}

int dev_ext_restart( void ) {
    device_extend_type_t dev = &_type_ext_dev;
    if(1 == dev_master_flag) {
        dev->status = 0;
        rt_pin_write(dev->ext_rdy_pin, PIN_HIGH);      //拉高ext_rdy,复位扩展设备
        io_ext_data_clear(&_ext_data);
        ext_dev_list_clear(&(dev->dev_list));
        modbus_rtu_destroy(&(dev->dev_ext));
        rt_pin_write(dev->ext_rdy_pin, PIN_LOW);        //初始化完成后输出低电平
        memheap_print();
        dev_ext_start();
    }
    return 0;
}

int dev_ext_get_status( void ) {
    device_extend_type_t dev = &_type_ext_dev;
    return dev->status;
}


void dev_ext_task_entry(void *parameter) {
    dev_delay(1000);
    dev_ext_start();
//    while(1){
//        dev_delay(1000);
//    }
}


static int dev_ext_task_init(void){
    //启动外设状态扫描线程
    rt_thread_t dev_ext_task = rt_thread_create("dev_ext_task",dev_ext_task_entry, NULL, 1024, 20, 10);
    if (dev_ext_task != RT_NULL) {
        rt_thread_startup(dev_ext_task);
    } else {
        LOG_E("dev_ext_task thread initialization failed!");
    }
    return 0;
}
INIT_APP_EXPORT(dev_ext_task_init);


#ifdef FINSH_USING_MSH
#include <finsh.h>
static rt_err_t ext_restart(int argc, char **argv) {
    dev_ext_restart();
    return RT_EOK;
}
MSH_CMD_EXPORT(ext_restart, set ext_restart);
#endif


